/**
 * Copyright  (c) 2009-2013 XLab,  Org. 
 * All  rights  reserved. 
 */
package org.xlab.baseframework.service.impl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Locale;

import org.apache.log4j.Logger;
import org.hibernate.classic.ValidationFailure;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.MessageSource;
import org.springframework.stereotype.Service;
import org.xlab.baseframework.annotation.Loggable;
import org.xlab.baseframework.dao.api.ISystemParameterDAO;
import org.xlab.baseframework.domain.ISystemRootUserDomain;
import org.xlab.baseframework.domain.ISystemUserDomain;
import org.xlab.baseframework.domain.SystemParameter;
import org.xlab.baseframework.domain.enumerate.EnumParameterType;
import org.xlab.baseframework.domain.view.SystemParameterVO;
import org.xlab.baseframework.event.SystemParameterChangedEvent;
import org.xlab.baseframework.event.SystemParameterRemovedEvent;
import org.xlab.baseframework.exception.ObjectNotFoundException;
import org.xlab.baseframework.exception.PermissionDeniedException;
import org.xlab.baseframework.exception.SystemParameterDuplicateException;
import org.xlab.baseframework.exception.SystemParameterNotFoundException;
import org.xlab.baseframework.exception.SystemRuntimeException;
import org.xlab.baseframework.exception.TooManyObjectsFoundException;
import org.xlab.baseframework.exception.ValidateFailedException;
import org.xlab.baseframework.service.api.ISystemParameterService;
import org.xlab.baseframework.system.SystemEventPublisher;
import org.xlab.baseframework.system.SystemRuntimeEnvironment;

/**
 * 系统参数服务接口实现
 * 
 * @author MaKuBEX Email:makubexnet@gmail.com
 * @version 1.0.0 Sep 9, 2010
 * @since 1.0.0
 * 
 */
@Service
public class SystemParameterServiceImpl implements ISystemParameterService {

	/**
	 * @serialField
	 */
	private static final long serialVersionUID = 1517943100779821269L;
	@Autowired(required = true)
	@Qualifier("bpf_systemParameterServiceMessageSource")
	private MessageSource messageSource;
	@Autowired(required = true)
	@Qualifier("bpf_systemParameterDAO")
	private ISystemParameterDAO systemParameterDAO;
	private static final Logger log = Logger
			.getLogger(SystemParameterServiceImpl.class);
	@Autowired(required = true)
	@Qualifier("bpf_systemEventPublisher")
	private SystemEventPublisher systemEventPublisher;

	/**
	 * @see org.xlab.baseframework.service.api.IBaseService#convertToVO(org.xlab.baseframework.domain.
	 *      INormalDomain )
	 */
	public SystemParameterVO convertToVO(SystemParameter entity) {
		String message = this.messageSource.getMessage(
				"info.service.syspara.converting",
				new Object[] { entity.toString() }, "Converting Pojo[" + entity
						+ "] to VO.", Locale.getDefault());
		log.debug(message);
		SystemParameterVO result = new SystemParameterVO(entity);
		return result;
	}

	/**
	 * @see org.xlab.baseframework.service.api.IBaseService#convertToVOList(java.util.Collection)
	 */
	public List<SystemParameterVO> convertToVOList(
			Collection<SystemParameter> entityCollection) {
		List<SystemParameterVO> result = new ArrayList<SystemParameterVO>(
				entityCollection.size());
		for (SystemParameter systemParameter : entityCollection) {
			result.add(this.convertToVO(systemParameter));
		}
		return result;
	}

	/**
	 * @see org.xlab.baseframework.service.api.ISystemParameterService#createAdvancedParameter(java.lang.String,
	 *      java.lang.String, java.lang.String, java.lang.String,
	 *      org.xlab.baseframework.domain.ISystemUserDomain)
	 */
	@Loggable
	public void createAdvancedParameter(String code, String value,
			String description, String remark, ISystemUserDomain createdBy)
			throws ValidateFailedException, SystemParameterDuplicateException {
		this.createParameter(EnumParameterType.ADVANCED, code.toUpperCase(),
				value, description, remark, createdBy);
	}

	/**
	 * @throws SystemParameterDuplicateException
	 * @see org.xlab.baseframework.service.api.ISystemParameterService#createBasicParameter(java
	 *      .lang.String, java.lang.String, java.lang.String, java.lang.String,
	 *      org.xlab.baseframework.domain.ISystemUserDomain)
	 */
	@Loggable
	public void createBasicParameter(String code, String value,
			String description, String remark, ISystemUserDomain createdBy)
			throws ValidateFailedException, SystemParameterDuplicateException {
		this.createParameter(EnumParameterType.BASIC, code, value, description,
				remark, createdBy);
	}

	private void createParameter(EnumParameterType type, String code,
			String value, String description, String remark,
			ISystemUserDomain createdBy) throws ValidateFailedException,
			SystemParameterDuplicateException {
		String message = null;
		message = this.messageSource.getMessage(
				"info.service.syspara.creating", new Object[] { type, code,
						value }, "Creating SYSTEM parameter[" + code
						+ "] value[" + value + "].", Locale.getDefault());
		log.debug(message);
		try {
			this.findByCode(code, false);
			message = this.messageSource.getMessage(
					"err.service.syspara.creating", null,
					"Error occured when creating system parameter!",
					Locale.getDefault());
			log.error(message);
			throw new SystemParameterDuplicateException(message);
		} catch (SystemParameterNotFoundException e) {
			Date current = new Date();
			SystemParameter param = new SystemParameter(type, code, value,
					description, remark, createdBy.getUserCode(),
					createdBy.getRealName());
			param.setCreatedTime(current);
			param.setCreatedBy(createdBy.getUserCode());
			param.setCreatedByName(createdBy.getRealName());
			param.setLastUpdatedBy(createdBy.getUserCode());
			param.setLastUpdatedTime(current);
			param.setLastUpdatedByName(createdBy.getRealName());
			try {
				this.systemParameterDAO.save(param);
				this.systemEventPublisher
						.publishEvent(new SystemParameterChangedEvent(this,
								param));
			} catch (ValidationFailure vf) {
				message = this.messageSource.getMessage(
						"message.valiexp.failed.param", new Object[] {
								code == null ? "code" : "value", null },
						"Validate domain failed before saving. Caused by field["
								+ code == null ? "code" : "value"
								+ "] value[null].", Locale.getDefault());
				log.error(message);
				throw new ValidateFailedException(message, vf);
			}
		}
	}

	/**
	 * @see org.xlab.baseframework.service.api.ISystemParameterService#createSystemParameter(java.lang.String,
	 *      java.lang.String, java.lang.String, java.lang.String,
	 *      org.xlab.baseframework.domain.ISystemRootUserDomain)
	 */
	@Loggable
	public void createSystemParameter(String code, String value,
			String description, String remark,
			ISystemRootUserDomain systemRootUser)
			throws ValidateFailedException, SystemParameterDuplicateException {
		this.createParameter(EnumParameterType.SYSTEM, code, value,
				description, remark, systemRootUser);
	}

	/**
	 * @see org.xlab.baseframework.service.api.ISystemParameterService#deleteParameter(org.xlab.baseframework.domain.SystemParameter)
	 */
	@Loggable
	public void deleteParameter(SystemParameter systemParameter)
			throws UnsupportedOperationException {
		String message = null;
		if (!systemParameter.getType().equals(EnumParameterType.BASIC)) {
			message = this.messageSource.getMessage(
					"message.usoexp.unsupported.remove.type",
					new Object[] { systemParameter.getClass(),
							systemParameter.getType() }, "The class["
							+ systemParameter.getClass()
							+ "] could not be removed, because of it\'s type["
							+ systemParameter.getType() + "].",
					Locale.getDefault());
			log.error(message);
			throw new UnsupportedOperationException(message);
		}
		this.systemParameterDAO.remove(systemParameter);
		this.systemEventPublisher.publishEvent(new SystemParameterRemovedEvent(
				this, systemParameter.getCode()));
	}

	/**
	 * @see org.xlab.baseframework.service.api.ISystemParameterService#findAll()
	 */
	public List<SystemParameter> findAll()
			throws SystemParameterNotFoundException {
		try {
			List<SystemParameter> results = this.systemParameterDAO.findAll();
			Collections.sort(results);
			return results;
		} catch (ObjectNotFoundException e) {
			throw new SystemParameterNotFoundException(e);
		}
	}

	/**
	 * @see org.xlab.baseframework.service.api.ISystemParameterService#getParameter(java.lang
	 *      .String)
	 */
	public SystemParameter findByCode(String code, boolean useCache)
			throws SystemRuntimeException, SystemParameterNotFoundException {
		SystemParameter result = null;
		String message = null;
		try {
			if (useCache) {
				message = this.messageSource.getMessage(
						"info.service.syspara.findingpara_cache",
						new Object[] { code },
						"Finding system parameter by code[" + code
								+ "] from cache...", Locale.getDefault());
				log.debug(message);
				result = SystemRuntimeEnvironment.getParameter(code);
			} else {
				message = this.messageSource.getMessage(
						"info.service.syspara.findingpara_db",
						new Object[] { code },
						"Finding system parameter by code[" + code
								+ "] from db...", Locale.getDefault());
				log.debug(message);
				result = this.systemParameterDAO.findByCode(code);
			}
		} catch (SystemRuntimeException re) {
			message = this.messageSource.getMessage(
					"message.srexp.notinitialized", null,
					"System has not been initialized.", Locale.getDefault());
			log.error(message);
			throw new SystemRuntimeException(message, re);
		} catch (SystemParameterNotFoundException e) {
			message = this.messageSource.getMessage(
					"message.spexp.notfound.by.code", new Object[] { code },
					"Could not find system parameter by code[" + code + "].",
					Locale.getDefault());
			log.error(message);
			throw e;
		} catch (ObjectNotFoundException e) {
			message = this.messageSource.getMessage(
					"message.spexp.notfound.by.code", new Object[] { code },
					"Could not find system parameter by code[" + code + "].",
					Locale.getDefault());
			log.warn(message);
			throw new SystemParameterNotFoundException(message, e);
		} catch (TooManyObjectsFoundException e) {
			throw new SystemRuntimeException(e);
		}
		return result;
	}

	/**
	 * @return the messageSource
	 */
	public MessageSource getMessageSource() {
		return messageSource;
	}

	/**
	 * @return the systemEventPublisher
	 */
	public SystemEventPublisher getSystemEventPublisher() {
		return systemEventPublisher;
	}

	/**
	 * @return the systemParameterDAO
	 */
	public ISystemParameterDAO getSystemParameterDAO() {
		return systemParameterDAO;
	}

	/**
	 * @see org.xlab.baseframework.service.api.IBaseService#parseFromVO(org.xlab.baseframework.domain.view.IViewDomainObject)
	 */
	public SystemParameter parseFromVO(SystemParameterVO viewObject) {
		SystemParameter pojo = new SystemParameter();
		BeanUtils.copyProperties(viewObject, pojo);
		return pojo;
	}

	/**
	 * @param messageSource
	 *            the messageSource to set
	 */
	public void setMessageSource(MessageSource messageSource) {
		this.messageSource = messageSource;
	}

	/**
	 * @param systemEventPublisher
	 *            the systemEventPublisher to set
	 */
	public void setSystemEventPublisher(
			SystemEventPublisher systemEventPublisher) {
		this.systemEventPublisher = systemEventPublisher;
	}

	/**
	 * @param systemParameterDAO
	 *            the systemParameterDAO to set
	 */
	public void setSystemParameterDAO(ISystemParameterDAO systemParameterDAO) {
		this.systemParameterDAO = systemParameterDAO;
	}

	/**
	 * @see org.xlab.baseframework.service.api.ISystemParameterService#updateNonSystemParameter(org.xlab.baseframework.domain.SystemParameter)
	 */
	@Loggable
	public void updateNonSystemParameter(SystemParameter systemParameter,
			ISystemUserDomain updatedBy) throws UnsupportedOperationException,
			SystemParameterNotFoundException, SystemRuntimeException,
			PermissionDeniedException {
		SystemParameter oldVersion = this.findByCode(systemParameter.getCode(),
				true);
		String message = null;
		if (oldVersion.getType() != systemParameter.getType()
				|| oldVersion.getType() == EnumParameterType.SYSTEM) {
			message = this.messageSource
					.getMessage(
							"message.usoexp.unsupported.update.type",
							new Object[] { "SystemParameter",
									oldVersion.getType() },
							"The class[SystemParameter] could not be updated, because of it's type["
									+ oldVersion.getType() + "]. ",
							Locale.getDefault());
			log.error(message);
			throw new UnsupportedOperationException(message);
		} else if (oldVersion.getType() == EnumParameterType.ADVANCED) {
			if (!(updatedBy instanceof ISystemRootUserDomain)) {
				message = this.messageSource.getMessage(
						"message.pdexp.permission.notenough", null,
						"No enough permission to access/execute.",
						Locale.getDefault());
				log.error(message);
				throw new PermissionDeniedException(message);
			}
		}
		systemParameter.setLastUpdatedBy(updatedBy.getUserCode());
		systemParameter.setLastUpdatedByName(updatedBy.getRealName());
		systemParameter.setLastUpdatedTime(new Date());
		message = this.messageSource.getMessage(
				"info.service.syspara.updating",
				new Object[] { systemParameter.getType(),
						systemParameter.getCode(), oldVersion.getValue(),
						systemParameter.getValue() }, "Updating "
						+ systemParameter.getType() + " parameter["
						+ systemParameter.getCode() + "] from value["
						+ oldVersion.getValue() + "] to value["
						+ systemParameter.getValue() + "].",
				Locale.getDefault());
		log.debug(message);
		this.systemParameterDAO.update(systemParameter);
	}
}
